﻿//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Description: Validation Rule to validate the specified XPath and its value.
//
//------------------------------------------------------------------------------
using System;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.Xml;
using HtmlAgilityPack;

namespace WMIS.Test.Common.VSTS.WebTest
{
    [DisplayName("XPath and Value")]
    [Description("Validates the specified XPath and its value.")]
    public class ValidateXPathValue : ValidationRule
    {
        // The XPath
        private string xPath;

        [DisplayName("Required XPath")]
        [Description("The full XPath having a valid syntax.")]
        public string RequiredXPath
        {
            get { return xPath; }
            set { xPath = value; }
        }

        // The value at the end of the XPath
        private string xPathValue;

        [DisplayName("XPath Value")]
        [Description("The expected value of the XPath.")]
        public string SpecifiedXPathValue
        {
            get { return xPathValue; }
            set { xPathValue = value; }
        }

        //  Should the Case be ignored while comparing the InnerText
        private bool ignoreCaseValue = false;

        [DisplayName("Ignore Case")]
        [Description("Whether case should be ignored or not while comparing the XPath Value.")]
        [DefaultValue(false)]
        public bool IgnoreCase
        {
            get { return ignoreCaseValue; }
            set { ignoreCaseValue = value; }
        }
    
        // Validate is called with the test case Context and the request context.
        // These allow the rule to examine both the request and the response.
        //---------------------------------------------------------------------
        public override void Validate(object sender, ValidationEventArgs e)
        {
            bool validated = false;
            bool nodeFound = false;

            try
            {
                if (e.Response.IsXml.Equals(true) && xPath.Length != 0)
                {
                    //  It's an XML response
                    XmlNodeList xmlNodeList = e.Response.XmlDocument.SelectNodes(xPath);

                    if (xmlNodeList.Count > 0)
                    {
                        nodeFound = true;
                    }

                    foreach (XmlNode xmlNode in xmlNodeList)
                    {
                        e.Message = String.Format("XPathValue = '{0}', xmlNode.InnerText = '{1}'", xPathValue, xmlNode.InnerText);
                        if (xPathValue.Length == 0 || String.Equals(xPathValue, xmlNode.InnerText.Trim(), (ignoreCaseValue == true) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
                        {
                            validated = true;
                            break;
                        }
                    }
                }
                else if (e.Response.IsHtml.Equals(true) && xPath.Length != 0)
                {
                    //It's an HTML response
                    HtmlAgilityPack.HtmlDocument mainDocument = new HtmlAgilityPack.HtmlDocument();
                    mainDocument.LoadHtml(e.Response.BodyString);

                    HtmlNode mainNode = mainDocument.DocumentNode;

                    if (mainNode.SelectNodes(xPath) != null)
                    {
                        foreach (HtmlNode htmlNode in mainNode.SelectNodes(xPath))
                        {
                            nodeFound = true;

                            e.Message = String.Format("XPathValue = '{0}', htmlNode.InnerText = '{1}'", xPathValue, htmlNode.InnerText);

                            if (xPathValue.Length == 0 || String.Equals(xPathValue, htmlNode.InnerText.Trim(), (ignoreCaseValue == true) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
                            {
                                validated = true;
                                break;
                            }
                            else
                            {
                                //  User would have specified an attribute in the XPath, let's check if the value matches with the attribute's value
                                foreach (HtmlAgilityPack.HtmlAttribute htmlAttribute in htmlNode.Attributes)
                                {
                                    e.Message = String.Format("XPathValue = '{0}', htmlAttribute.Value = '{1}'", xPathValue, htmlAttribute.Value);

                                    if (String.Equals(xPathValue, htmlAttribute.Value.ToString(), (ignoreCaseValue == true) ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))
                                    {
                                        validated = true;
                                        break;
                                    }
                                }

                                //  We don't want to continue with the outer for loop if we've found a match
                                if (validated == true)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }

                e.IsValid = validated;

                // If the validation fails, set the error text that the user sees
                if (!validated)
                {
                    if (nodeFound == false)
                    {
                        e.Message = String.Format("The specified XPath '{0}' was not found", xPath.ToString());
                    }
                    else
                    {
                        //  Above, we're showing the user what was expected and what was found
                    }
                }
            }
            catch (Exception ex)
            {
                e.IsValid = validated;
                e.Message = String.Format("An exception occurred. Please check the input XPath: '{0}' and Value: '{1}'. Stack Dump: {2}", xPath, xPathValue, ex);
            }
            
            
        }
        
    }
}